package com.zl.autocode.common.utils.generator;


import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.BooleanLiteralExpr;
import com.github.javaparser.ast.expr.MemberValuePair;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.zl.autocode.common.constant.AutocodeConfigC;
import com.zl.autocode.common.utils.generator.model.ConvertColumn;
import com.zl.autocode.common.utils.generator.model.ConvertConfig;
import com.zl.autocode.common.utils.generator.model.ConvertTable;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component("javabean"+AutocodeConfigC.GENERATOR_READER_KEY)
public class ReaderJavabean implements IReaderData<ConvertConfig> {
    
    private static String DEFAULT_TYPE = "默认分类";
    private static String SPLIT_RULE = "(-|:)+";//拆分条件
   
    private static JavaParser jParser = new JavaParser();
    
    @Override
    public void parseData(ConvertConfig configParam) throws Exception {
        // TODO Auto-generated method stub
        log.info("javabean加载方式");
        long start = System.currentTimeMillis();
        
        File file = new File(configParam.getSqlPath());
        
        List<ConvertTable>  tableLists = new ArrayList<>();
        List<String> hasCodeList = new ArrayList<>();
        
        if(file.exists()){
            if(file.isDirectory()){
                File[] allFiles = file.listFiles();
                for(File f: allFiles){
                    if(f.isFile()){
                        readerEntry(configParam,tableLists,hasCodeList,f.getPath(),true);
                    }
                }
            }else{
                readerEntry(configParam,tableLists,hasCodeList,configParam.getSqlPath(),true);
            }
        }
        
        log.info("Data loading use time:" + (System.currentTimeMillis() - start) / 1000F + "s");
    }
    
    @Override
    public Map<String,Object> getData(ConvertConfig configParam) throws Exception {
        long start = System.currentTimeMillis();
        
        Map<String,Object> result = new HashMap<>();
        
        List<ConvertTable>  tableLists = new ArrayList<>();
        List<String> hasCodeList = new ArrayList<>();
        
        File file = new File(configParam.getSqlPath());
        if(file.exists()){
            if(file.isDirectory()){
                File[] allFiles = file.listFiles();
                for(File f: allFiles){
                    if(f.isFile()){
                        readerEntry(configParam,tableLists,hasCodeList,f.getPath(),false);
                    }
                }
            }else{
                readerEntry(configParam,tableLists,hasCodeList,configParam.getSqlPath(),false);
            }
        }
        result.put("tables", configParam.getTbls());
        
        log.info("Data loading use time:" + (System.currentTimeMillis() - start) / 1000F + "s");
        
        return result;
    }
    
    public static void readerEntry(ConvertConfig configParam, List<ConvertTable> tableLists,List<String> hasCodeList,String path,boolean tableFlag) throws Exception {
      try{
          String content = FileUtil.readUtf8String(path);
          ParseResult<CompilationUnit> result = jParser.parse(content);
          CompilationUnit cUnit = result.getResult().get();
          
          ConvertTable table = new ConvertTable();
          List<ConvertColumn> allcols = new ArrayList<>();
          table.setAllcols(allcols);
          cUnit.accept(new MethodVisitor(), table);
          String tableCode = table.getTableCode();
          //如果有选中的表,则只生成选中的表,否则全部加载
          List<String> selectedTables = configParam.getLoadTables();
          String queryType = StringUtils.isNotBlank(configParam.getQueryType())?configParam.getQueryType():"0";
          if(selectedTables != null && selectedTables.size()>0 
                  &&(("0".equals(queryType) && !selectedTables.stream().anyMatch(x-> x.equalsIgnoreCase(tableCode)))
                     || !selectedTables.stream().anyMatch(x-> tableCode.contains(x.toLowerCase())))){
              return;
          }
          if(hasCodeList.contains(tableCode)){
              log.info("存在重复对象>>{}",tableCode);
              return;
          }
          hasCodeList.add(tableCode);
          tableLists.add(table);
      }catch(Exception e){
          log.error("Table loading error>>{},{}",path,e);
      }
    }
    
    private static  class MethodVisitor extends VoidVisitorAdapter<ConvertTable> {
        @Override
        public void visit(ClassOrInterfaceDeclaration n, ConvertTable arg) {
            String tableCode = StrUtil.toUnderlineCase(n.getNameAsString()).toLowerCase();
            arg.setTableName(tableCode);
            arg.setTableCode(tableCode);
            arg.setTableTag(DEFAULT_TYPE);
            Optional<AnnotationExpr> amodel = n.getAnnotationByClass(ApiModel.class);
            if(amodel !=null){
               AnnotationExpr anner = amodel.get();
               List<Node> nodes = anner.getChildNodes();
               for(Node nodesx:nodes){
                   if(nodesx instanceof MemberValuePair){
                       MemberValuePair mem = (MemberValuePair)nodesx;
                       if("value".equals(mem.getNameAsString())){
                           String[] titleArr = ((StringLiteralExpr)mem.getValue()).getValue().split(SPLIT_RULE);
                           arg.setTableTitle(titleArr.length>1?titleArr[1]:titleArr[0]);
                           arg.setTableTag(titleArr.length>1?titleArr[0]:DEFAULT_TYPE);
                           break;
                       }
                   }
               }
            }
            super.visit(n, arg);
        }
        @Override
        public void visit(FieldDeclaration n, ConvertTable arg) {
            if(!n.getVariables().toString().contains("serialVersionUID")){
                List<ConvertColumn> allcols = arg.getAllcols();
                ConvertColumn col = new ConvertColumn();
                allcols.add(col);
                String columnCode = StrUtil.toUnderlineCase(n.getVariables().get(0).getNameAsString()).toLowerCase();
                col.setColumnCode(columnCode);
                col.setColumnName(columnCode);
                col.setColumnDatatype(n.getElementType().asString());
                col.setColumnMandatory("1");
                col.setColumnPrimarykey("0");
                String columnDatatype = col.getColumnDatatype().toLowerCase();
                if(columnDatatype.equalsIgnoreCase("Instant") || columnDatatype.equalsIgnoreCase("Date")){
                    col.setColumnDatatype("Instant");
                }
                else if(columnDatatype.equalsIgnoreCase("double") || columnDatatype.equalsIgnoreCase("float")
                        ||columnDatatype.equalsIgnoreCase("int")|| columnDatatype.equalsIgnoreCase("integer")
                        ||columnDatatype.equalsIgnoreCase("long")|| columnDatatype.equalsIgnoreCase("short")
                        ||columnDatatype.equalsIgnoreCase("BigDecimal")){
                    col.setColumnDatatype("BigDecimal");
                }else{//剩余都处理为String
                    col.setColumnDatatype("String");
                    col.setColumnLength("100");
                }
                Optional<AnnotationExpr> amodel = n.getAnnotationByClass(ApiModelProperty.class);
                if(amodel !=null){
                   AnnotationExpr anner = amodel.get();
                   if(anner != null && anner.getChildNodes() != null){
                       List<Node> nodes = anner.getChildNodes();
                       for(Node nodesx:nodes){
                           if(nodesx instanceof MemberValuePair){
                               MemberValuePair mem = (MemberValuePair)nodesx;
                               if("value".equals(mem.getNameAsString())){
                                   col.setColumnTitle(((StringLiteralExpr)mem.getValue()).getValue());
                               }else if("required".equals(mem.getNameAsString())){
                                   col.setColumnMandatory(((BooleanLiteralExpr)mem.getValue()).getValue()?"0":"1");
                               }
                               else if("position".equals(mem.getNameAsString())){
                                   col.setColumnPrimarykey(mem.getValue().toString().equals("-1")?"1":"0");
                               }
                               else if("example".equals(mem.getNameAsString())){
                                   col.setColumnComment(((StringLiteralExpr)mem.getValue()).getValue());
                               }
                           }
                       }
                   }
                }
                if(StringUtils.isBlank(col.getColumnTitle()) && n.getComment()!=null){
                    String columnTitle = n.getComment().toString().replaceAll("//|\\*|/\\s", "").trim();
                    col.setColumnComment(columnTitle);
                    if(columnTitle.length() > 50){
                        columnTitle = columnTitle.substring(0, 50);
                    }
                    col.setColumnName(columnTitle);
                }
            }
            super.visit(n, arg);
        }
    }
}
